package edu.hawaii.jmotif.sequitur.logic;

import java.util.Iterator;
import java.util.StringTokenizer;
import edu.hawaii.jmotif.sax.SAXFactory;
import edu.hawaii.jmotif.sax.alphabet.Alphabet;
import edu.hawaii.jmotif.sax.alphabet.NormalAlphabet;
import edu.hawaii.jmotif.sax.datastructures.SAXFrequencyData;
import edu.hawaii.jmotif.sax.datastructures.SAXFrequencyEntry;
import edu.hawaii.jmotif.timeseries.Timeseries;

/**
 * Handling of data operations for the chart
 * 
 * @author Manfred Lerner
 * 
 */
public class MotifChartData {
  Alphabet normalA = new NormalAlphabet();

  private final int saxWindowSize;
  private final int saxAlphabetSize;
  private final int saxPAASize;
  private final Timeseries originalTimeSeries;

  private SAXFrequencyData saxFrequencyData = new SAXFrequencyData();

  int frequencyCounter = 0;
  String previousString = "";

  private String saxDisplayString = null;
  private String _NumericSAX = null;

  private String _strSequitur = null;

  private SAXRule currentRule = null;

  /**
   * Constructor
   * 
   * @param originalData the time series.
   * @param windowSize SAX window size.
   * @param alphabetSize SAX alphabet size.
   * @param paaSize SAX PAA size.
   */
  public MotifChartData(Timeseries originalData, int windowSize, int alphabetSize, int paaSize) {
    originalTimeSeries = originalData;
    saxWindowSize = windowSize;
    saxAlphabetSize = alphabetSize;
    saxPAASize = paaSize;
  }

  /**
   * Constructor.
   * 
   * @param originalData the time series.
   * @param windowSize SAX window size.
   * @param alphabetSize SAX alphabet size.
   * @param paaSize SAX PAA size.
   * @param freqData SAXFrequencyData.
   */
  public MotifChartData(Timeseries originalData, int windowSize, int alphabetSize, int paaSize,
      SAXFrequencyData freqData) {
    originalTimeSeries = originalData;
    saxWindowSize = windowSize;
    saxAlphabetSize = alphabetSize;
    saxPAASize = paaSize;
    saxFrequencyData = freqData;
  }

  /**
   * Get the time series.
   * 
   * @return original time series
   */
  public Timeseries getOriginalTimeseries() {
    return originalTimeSeries;
  }

  /**
   * @return SAX window size
   */
  public int getSAXWindowSize() {
    return saxWindowSize;
  }

  /**
   * @return SAX alphabet size
   */
  public int getSAXAlphabetSize() {
    return saxAlphabetSize;
  }

  /**
   * @return SAX PAA size
   */
  public int getSAXPaaSize() {
    return saxPAASize;
  }

  /**
   * @return SAX frequency data
   */
  public SAXFrequencyData getFreqData() {
    return saxFrequencyData;
  }

  // /**
  // * convert a SAX string 'abcd' to '1234'
  // *
  // * @return SAX string with numeric alphabet
  // * @throws CloneNotSupportedException
  // */
  // public Timeseries getSaxAsNumericValues() throws CloneNotSupportedException {
  //
  // Timeseries numericTs = null;
  //
  // String strSAX = new String();
  //
  // Iterator<SAXFrequencyEntry> freqIterator = this.saxFrequencyData.iterator();
  // while (freqIterator.hasNext()) {
  // SAXFrequencyEntry freqEntry = freqIterator.next();
  // strSAX += freqEntry.getSubstring();
  // }
  //
  // try {
  // strSAX = SAXFactory.ts2string(originalTimeSeries, 300, normalA, saxAlphabetSize);
  //
  // double dVals[] = convertString2Numeric(strSAX);
  // long lVals[] = new long[dVals.length];
  // for (int index = 0; index < lVals.length; index++) {
  // lVals[index] = index;
  // }
  //
  // numericTs = new Timeseries(dVals, lVals);
  // }
  // catch (TSException e) {
  // // TODO Auto-generated catch block
  // e.printStackTrace();
  // }
  //
  // return numericTs;
  // }

  // private double[] convertString2Numeric(String strSAX) {
  // double retVal[] = new double[strSAX.length()];
  // for (int index = 0; index < strSAX.length(); index++) {
  // char c = strSAX.charAt(index);
  // retVal[index] = c - 'a' + 1;
  // }
  // return retVal;
  // }

  /**
   * build the SAX frequencies
   */
  public void buildSAX() {
    try {
      
      // process the time series getting SAX words out
      //
      // @TODO *** in internals of this method I use not MINDIST but only EXACT MATCH
      //
      saxFrequencyData = SAXFactory.ts2saxZnormByCuts(originalTimeSeries, saxWindowSize,
          saxPAASize, normalA.getCuts(saxAlphabetSize));
      
      // debug output
      //
      Iterator<SAXFrequencyEntry> freqIterator = saxFrequencyData.iterator();
      while (freqIterator.hasNext()) {
        SAXFrequencyEntry freqEntry = freqIterator.next();
        System.out.println(freqEntry.getSubstring() + " - SAX Index: " + freqEntry.getEntries());
      }

      // get a whole series representation as SAX words
      //
      saxDisplayString = saxFrequencyData.getSAXString(" ");
      System.out.println("SAX Display String: " + saxDisplayString);

      // weird conversion to numbers begins
      _NumericSAX = saxDisplayString;

      boolean bPavel = true;
      if (bPavel) {
       
        for (int i = 0; i < saxAlphabetSize; i++) {
          char c1 = (char) ('a' + i);
          char c2 = (char) ('1' + i);
          _NumericSAX = _NumericSAX.replace(c1, c2);
        }

      }
      else {
        
        for (int i = 0; i < saxAlphabetSize; i++) {
          char c1 = (char) ('a' + saxAlphabetSize - i - 1);
          char c2 = (char) ('1' + i);
          _NumericSAX = _NumericSAX.replace(c1, c2);
          
        }
      }

      // call to sequitur
      _strSequitur = runSequitur(_NumericSAX);
      System.out.println("SAX Numeric String: " + _NumericSAX);
    }
    catch (Exception ex) {
      ex.printStackTrace();
    }
  }

  /**
   * run the sequitur algorithm for SAX
   * 
   * @param strSAX string of SAX subsequences
   * @return the sequitur rules as string
   */
  public String runSequitur(String strSAX) {

    //SAXRule firstRule = new SAXRule();
    currentRule = new SAXRule(this);
    // Reset number of rules and Hashtable.

    SAXRule.numRules = 0;
    SAXSymbol.theDigrams.clear();
    StringTokenizer st = new StringTokenizer(strSAX, " ");
    while (st.hasMoreTokens()) {
      //firstRule.last().insertAfter(new SAXTerminal(st.nextToken()));
      //firstRule.last().p.check();
      currentRule.last().insertAfter(new SAXTerminal(st.nextToken()));
      currentRule.last().p.check();
    }

    //currentRule = firstRule;

    //return firstRule.getSAXRules().toString();
    return currentRule.getSAXRules().toString();
  }
  
  /**
   *  converts rules from a foreign alphabet to the internal original SAX alphabet
   * 
   * @param rule the SAX rule in foreign SAX alphabet
   * @return the SAX string in original alphabet, e.g. aabbdd
   */
  public String convert2OriginalSAXAlphabet(char firstForeignAlphabetChar, String rule) {
  	String textRule = rule;
      for (int i = 0; i < getSAXAlphabetSize(); i++) {
        char c1 = (char) (firstForeignAlphabetChar + i);
        char c2 = (char) ('a' + i);
        textRule = textRule.replace(c1, c2);
      }
  	return textRule;
  }

  /**
   * @return sequitur string
   */
  public String getSequiturString() {
    return _strSequitur;
  }

  /**
   * @param strSequitur sequitur string
   */
  public void setSequiturString(String strSequitur) {
    _strSequitur = strSequitur;
  }

  /**
   * @return SAX display formatted string
   */
  public String getSAXDisplay() {
    return saxDisplayString;
  }

  /**
   * @param SAXDisplay SAX display formatted string
   */
  public void setSAXDisplay(String SAXDisplay) {
    saxDisplayString = SAXDisplay;
  }

  /**
   * @return SAX string in numerical format
   */
  public String getNumericSAX() {
    return _NumericSAX;
  }

  /**
   * @param numericSAX SAX string in numerical format
   */
  public void setNumericSAX(String numericSAX) {
    _NumericSAX = numericSAX;
  }

  /**
   * @return currently selected rule
   */
  public SAXRule getCurrentRule() {
    return currentRule;
  }
}
